#define _TOWER_
#ifdef _TOWER_
#include <sstream>
#include <map>
#include <queue>
#include "AirStripStack.h"
#include "ControllerStack.h"
#include "SharedCounter.h"
#include "ReadFifo.h"
#include "WriteFifo.h"
#include "SIGINT_Handler.h"
#include "SIGPIPE_Handler.h"
#include "SignalHandler.h"
#include "Logger.h"
#include "Message.h"
#include "Config.h"

void handleMessage(const Message& msg, std::queue<Message> &para_despegar,
		std::queue<Message> &para_aterrizar) {
	if (msg.getType() == 1) {
		para_despegar.push(msg);
	} else {
		para_aterrizar.push(msg);
	}
}

int main(int argc, char* argv[])
{
	std::queue<Message> para_aterrizar;
	std::queue<Message> para_despegar;
	Logger::init();
	//tomar parametros de entrada
	unsigned int amountOfAirstrips = Config::get_instance().get_int(
			"airstrips");
	unsigned int amountOfControllers = Config::get_instance().get_int(
			"controllers");
	char buffer[20];
	std::map<unsigned long, WriteFifo*> map;

	//Inicializacion
	Logger::getInstance().setProcessInformation(std::string("Tower").c_str());

	// event handler para la senial SIGINT (-2)
	SIGINT_Handler sigint_handler;
	SIGPIPE_Handler sigpipe_handler;

	// se registra el event handler declarado antes
	SignalHandler::getInstance()->registerHandler(SIGINT, &sigint_handler);
	SignalHandler::getInstance()->registerHandler(SIGPIPE, &sigpipe_handler);

	AirStripStack airstrips(AirStripStack::defaul_path, amountOfAirstrips);
	ControllerStack controllers(ControllerStack::defaul_path,
			amountOfControllers);
	SharedCounter counter(SharedCounter::default_path, 'a');

	//Abrir fifo de lectura con aviones
	const std::string fifo_name = "./tmp/fifo_requests";
	Logger::getInstance().logMessage(Logger::DEBUG,
			"Instanciando fifo: " + fifo_name);
	ReadFifo reading_fifo(fifo_name);
	Logger::getInstance().logMessage(Logger::DEBUG,
			"Abriendo fifo: " + fifo_name);
	reading_fifo.open_();

	std::string name = "./tmp/Tower_Contr_";

	//Abrir fifo de escritura con cada controlador
	for (unsigned int i = 1; i <= amountOfControllers; ++i) {
		std::stringstream s;
		s << name;
		s << i;
		Logger::getInstance().logMessage(Logger::DEBUG,
				"Instanciando fifo: " + s.str());
		map[i] = new WriteFifo(s.str()); //fifo_x;
		s.clear();
	}
	Logger::getInstance().logMessage(Logger::DEBUG, "Fifos instanciados");

	std::map<unsigned long, WriteFifo*>::iterator it = map.begin();
	for (; it != map.end(); ++it) {
		Logger::getInstance().logMessage(Logger::DEBUG, "abriendo fifo");
		(*it).second->open_();
	}
	Logger::getInstance().logMessage(Logger::DEBUG, "Fifos abiertos");

	try {
		Semaforo msg_recibidos(Semaforo::generarClave("/dev/null",'z'));
		unsigned long controlerId = 0;
		unsigned int type = 0;
		unsigned long planeID = 0;
		bool encolados = false;
		bool any_controller_free = false;

		Logger::getInstance().logMessage(Logger::TRACE, "iniciando la simulacion");
		// mientras no se reciba la senial SIGINT, el proceso realiza su trabajo
		while (sigint_handler.getGracefulQuit() == 0) {
			//simulacion
			//si hay mensajes encolados
			encolados = (!para_aterrizar.empty()) || (!para_despegar.empty());

			any_controller_free = !controllers.isEmpty();
			while (any_controller_free && encolados) {
				if (!para_aterrizar.empty()) {
					type = para_aterrizar.front().getType();
					planeID = para_aterrizar.front().getData();
					para_aterrizar.pop();
					Logger::getInstance().logMessage(Logger::TRACE,
							"delegando mensaje");
				} else if (!para_despegar.empty()) {
					type = para_despegar.front().getType();
					planeID = para_despegar.front().getData();
					para_despegar.pop();
					Logger::getInstance().logMessage(Logger::TRACE,
							"delegando mensaje");
				} else {
					break;
					Logger::getInstance().logMessage(Logger::ERROR, "Desparecen mensajes encolados.");
				}
				controlerId = controllers.pop();
				std::stringstream s;
				s << "ocupando controlador " << controlerId <<
						" se envía mensaje " << planeID << " del tipo " << type;
				Logger::getInstance().logMessage(Logger::TRACE,	s.str());
				s.clear();
				Message msg_salida(type, planeID);
				char *mensaje = msg_salida.serialize();
				map.find(controlerId)->second->write_(mensaje, msg_salida.size());
				Logger::getInstance().logMessage(Logger::TRACE, "mensaje enviado");
				delete[] mensaje;
				mensaje = NULL;
				any_controller_free = !controllers.isEmpty();
				encolados = (!para_aterrizar.empty()) || (!para_despegar.empty());
			}

			//verificar si hay mensajes entrantes leyendo un contador compartido.
			int entrantes = counter.reed();
			if (entrantes > 0)
			{
				//leer esa cantidad de mensajes del fifo (en ese momento va haber esa cantidad o mas) y encolarlos
				for (int i = 0; i < entrantes; ++i)
				{
					try {
						msg_recibidos.p();
					} catch (std::string & e) {
						Logger::getInstance().logMessage(Logger::DEBUG, e);
					}
					int rd = reading_fifo.read_(buffer,
							sizeof(unsigned int) + sizeof(unsigned long));
					Message msg(buffer,
							sizeof(unsigned int) + sizeof(unsigned long));
					//rd da -1 en caso de error
					if (rd == -1) {
						Logger::getInstance().logMessage(Logger::ERROR,
								std::string("Error al leer mensaje"));
						break;
					} else if (rd == 0)
					/*rd da 0 en caso de leer un EOF que significa que el
					archivo solo está abierto en modo lectura por este proceso */
					{
						Logger::getInstance().logMessage(Logger::DEBUG, "No hay aviones");
						continue;
					} else
					//lee correctamente
					{
						if (rd < msg.size()) {
							Logger::getInstance().logMessage( Logger::ERROR,
									std::string("Error al leer mensaje"));
							break;
						}
						//reducir cantidad de mensajes a leer
						counter.decrement();
						std::stringstream s;
						s << msg.getData();
						Logger::getInstance().logMessage(Logger::DEBUG, "data del msj: " + s.str());
						s.clear();
						//encolar
						if (msg.getType() == 1)
							para_despegar.push(msg);
						else
							para_aterrizar.push(msg);
					}
				}
			}
			encolados = (!para_aterrizar.empty()) || (!para_despegar.empty());
			//Si no hay encolados la torre se autobloquea leyendo del pipe
			if( ! encolados) {
				//hacer una lectura del fifo (bloquea automaticamente el proceso hasta que llegue un mensaje)
				try {
					msg_recibidos.p();
				} catch (std::string & e) {
					Logger::getInstance().logMessage(Logger::DEBUG, e);
				}
				int rd = reading_fifo.read_(buffer,
						sizeof(unsigned int) + sizeof(unsigned long));
				Message msg(buffer,
						sizeof(unsigned int) + sizeof(unsigned long));
				//rd da -1 en caso de error
				if (rd == -1) {
					Logger::getInstance().logMessage( Logger::ERROR,
							std::string("Error al leer mensaje"));
					break;
				} else if (rd == 0)
				//rd da 0 en caso de leer un EOF que significa que el
				//archivo solo está abierto en modo lectura por este proceso
				{
					continue;
				} else
				//rd da >0 cuando lee correctamente
				{
					if (rd < msg.size()) {
						Logger::getInstance().logMessage(Logger::ERROR,
								std::string("Error al leer mensaje"));
						break;
					}
					//reducir cantidad de mensajes a leer
					counter.decrement();
					Logger::getInstance().logMessage(Logger::DEBUG, std::string("DATA"));
					std::stringstream s;
					s << msg.getData();
					Logger::getInstance().logMessage(Logger::DEBUG, std::string("data del msj: " + s.str()));
					s.clear();
					//encolar
					if (msg.getType() == 1)
						para_despegar.push(msg);
					else
						para_aterrizar.push(msg);
				}
			}
		}
		Logger::getInstance().logMessage(Logger::DEBUG, "graceful quit");

		// se recibio la senial SIGINT, el proceso termina
		SignalHandler::destroy();

		std::map<unsigned long, WriteFifo*>::iterator it_2 = map.begin();
		for (; it_2 != map.end(); ++it_2) {
			(*it_2).second->close_();
			(*it_2).second->delete_();
		}
		reading_fifo.close_();
		reading_fifo.delete_();
	} catch (Exception & e) {
		Logger::getInstance().logMessage(Logger::ERROR, e.get_error_description());
		return -1;
	} catch (std::string & e) {
		Logger::getInstance().logMessage(Logger::ERROR, e);
		return -1;
	}
	return 0;
}

#endif
